/*
 * ccid.h
 *
 *  Created on: Jul 15, 2013
 *      Author: g.kruglov
 */

#ifndef CCID_H_
#define CCID_H_

#define SLOT0_ICC_ABSENT    0x00
#define SLOT0_ICC_PRESENT   0x01
#define SLOT0_CHANGED       0x02

#include <inttypes.h>
#include <string.h>			// memcpy
#include "ch.h"
#include "msgbox.h"
#include "buf_sizes.h"


struct ccid_abProtocolDataStructureT1_t {
    uint8_t bmFindexDindex;
    uint8_t bmTCCKST1;
    uint8_t bGuardTimeT1;
    uint8_t bmWaitingIntegersT1;
    uint8_t bClockStop;
    uint8_t bIFSC;
    uint8_t bNadValue;
} __attribute__ ((__packed__));

class CCID_t {
private:
    uint8_t QueueBuf[CCID_QUEUE_SZ];
    InputQueue iqueue;
    union {
        struct {
            CcidHeader_t HeaderIN;
            uint8_t CrdBuf[CCID_DATA_SZ];
        };
        uint8_t ComboBuf[CCID_HEADER_SZ + CCID_DATA_SZ];
    } __attribute__ ((__packed__));
    CcidHeader_t HeaderOUT;
    ccid_abProtocolDataStructureT1_t ParamsT1;
    void FlushQueue() { for(uint32_t i=0; i<HeaderOUT.dwLength; i++) chIQGetTimeout(&iqueue, MS2ST(99)); }
    void Transmit();
    // Commands PC to RDR
    void PcToRdrXfrBlock(ccMsg_t *PMsg);
    // Commands RDR to PC
    void RdrToPcSlotStatus();
    void RdrToPcParameters();
    void RdrToPcCmdNotSupported(uint8_t AMsgType);
    void RdrToPcXfrBlockNoData(uint8_t Status, uint8_t Error);
public:
    void Init();
    void UsbOutTask();
    void UsbInTask();
    // Card interface
    MsgBox_t<ccMsg_t, CCID_MSGBOX_SZ> MsgBox;
    // Commands RDR to PC
    void NotifyTimeExtension(uint8_t AExtentValue);
    void NotifySlotChange(uint8_t AState);
    void DataReplyCallback(uint8_t *PData, uint16_t ALen);
};

extern CCID_t Ccid;

// ============================ Defines ========================================
// PC to READER messages
#define PC_to_RDR_IccPowerOn        0x62
#define PC_to_RDR_IccPowerOff       0x63
#define PC_to_RDR_GetSlotStatus     0x65
#define PC_to_RDR_XfrBlock          0x6F
#define PC_to_RDR_GetParameters     0x6C
#define PC_to_RDR_ResetParameters   0x6D
#define PC_to_RDR_SetParameters     0x61
#define PC_to_RDR_Escape            0x6B
#define PC_to_RDR_IccClock          0x6E
#define PC_to_RDR_T0APDU            0x6A
#define PC_to_RDR_Secure            0x69
#define PC_to_RDR_Abort             0x72
#define PC_to_RDR_SetDataRateAndClockFrequency  0x73

// READER to PC messages
#define RDR_to_PC_DataBlock         0x80
#define RDR_to_PC_SlotStatus        0x81
#define RDR_to_PC_Parameters        0x82
#define RDR_to_PC_Escape            0x83
#define RDR_to_PC_DataRateAndClockFrequency 0x84

// Interrupt-IN messages
#define RDR_to_PC_NotifySlotChange  0x50
#define RDR_to_PC_HardwareError     0x51

// Non-standard messages, used for Card answering UsbInTask
#define CRD_to_CCID_IccPowerOn      0x09
#define CRD_to_CCID_IccPowerOff     0x11
#define CRD_to_CCID_XfrBlock        0x18
#define CRD_to_CCID_NTS             0x19

// Error codes (p.54)
#define NO_ERROR                    0
#define CMD_NOT_SUPPORTED           0x00    // Yes, really 0x00
#define CMD_SLOT_BUSY               0xE0    // A second command was sent to a slot which was already processing a command
#define HW_ERROR                    0xFB    // An all inclusive hardware error occurred
#define ICC_MUTE                    0xFE    // CCID timed out while talking to the ICC
#define BAD_DWLENGTH                0x01
#define BAD_WLEVELPARAMETER         0x08
// Slot status register (p.55)
#define ICC_STATUS_ACTIVE           0x00
#define ICC_STATUS_INACTIVE         0x01
#define ICC_STATUS_NO_ICC_PRESENT   0x02
#define CMD_STATUS_OK               0x00    // (0)
#define CMD_STATUS_FAILED           0x40    // (1)
#define CMD_STATUS_TIMEEXTENSION    0x80    // (2)

// Clock status
#define CLK_RUNNING                 0x00
#define CLK_STOPPED_L               0x01

/* ==== CCID Class-Specific Control Requests ====
 * Page 23
 */
#define ABORT_REQ_TYPE              0b00100001
#define ABORT_REQ                   0x01
#define GET_CLOCK_FREQS_REQ_TYPE    0b10100001
#define GET_CLOCK_FREQS_REQ         0x02
#define GET_DATA_RATES_REQ_TYPE     0b10100001
#define GET_DATA_RATES_REQ          0x03


#endif /* CCID_H_ */
